package org.wggds.webservices.io;

import java.io.IOException;
import java.io.StringWriter;
import java.util.List;
import java.util.UUID;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.wggds.webservices.io.data.BiologicalContext;
import org.wggds.webservices.io.data.QueryResult;
import org.wggds.webservices.io.data.WggdsError;

public class WggdsResultUtilXml implements IWggdsResultUtil
{
    private String m_namespace = null;
    private String m_baseUrl = null;
    private StringBuffer m_resultBuffer = new StringBuffer();
    private boolean m_streamMode = false;
    private boolean m_writtenErrors = false;
    private boolean m_finished=false;

    public WggdsResultUtilXml(String a_namespace, String a_baseUrl)
    {
        this.m_namespace = a_namespace;
        this.m_baseUrl = a_baseUrl;
    }

    public String createResult( String a_listId) throws WggdsResultException
    {
        this.startRecordResult(a_listId);
        return finishRecordResult();        
    }
    
    public String createResult(List<QueryResult> a_resultList, String a_listId, boolean a_completeInformation, String a_glydeVersion) throws WggdsResultException
    {
        this.startRecordResult(a_listId, a_glydeVersion);
        for (QueryResult t_queryResult : a_resultList)
        {
            this.addResult(t_queryResult, a_completeInformation);
        }
        return finishRecordResult();        
    }

    public String createError(List<WggdsError> a_error) throws WggdsResultException
    {
        Element t_root = new Element("queryResult");
        t_root.setAttribute("baseURL",this.m_baseUrl);
        t_root.setAttribute("namespace",this.m_namespace);
        for (WggdsError t_wggdsError : a_error)
        {
            Element t_error = new Element("error");
            t_error.setAttribute("id",t_wggdsError.getId().getId().toString());
            Element t_message = new Element("message");
            t_message.setText(t_wggdsError.getMessage());
            t_error.addContent(t_message);
            t_root.addContent(t_error);
        }
        Document t_document = new Document(t_root);
        Format t_objFormat = Format.getPrettyFormat();
        XMLOutputter t_exportXML = new XMLOutputter(t_objFormat);
        StringWriter t_writer = new StringWriter();
        try
        {
            t_exportXML.output(t_document, t_writer );
        } 
        catch (IOException t_exception)
        {
            throw new WggdsResultException(t_exception.getMessage(),t_exception);
        }
        return t_writer.toString();
    }

    public void startRecordResult(String a_listId) throws WggdsResultException
    {
        this.startRecordResult(a_listId, null);
    }

    public void startRecordResult(String a_listId, String a_glydeVersion) throws WggdsResultException
    {
        if ( this.m_streamMode )
        {
            throw new WggdsResultException("Stream mode is already active. Call finishRecordResult first.");
        }
        this.m_streamMode = true;
        this.m_writtenErrors = false;
        this.m_resultBuffer = new StringBuffer();
        this.m_resultBuffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
        this.m_resultBuffer.append("<queryResult id=\"");
        this.m_resultBuffer.append(a_listId);
        this.m_resultBuffer.append("\" baseURL=\"");
        this.m_resultBuffer.append(this.m_baseUrl);
        this.m_resultBuffer.append("\" namespace=\"");
        this.m_resultBuffer.append(this.m_namespace);
        this.m_resultBuffer.append("\"");
        if ( a_glydeVersion != null )
        {
            this.m_resultBuffer.append(" glydeVersion=\"");
            this.m_resultBuffer.append(a_glydeVersion);
            this.m_resultBuffer.append("\"");
        }
        this.m_resultBuffer.append(">\n");
    }

    public String finishRecordResult() throws WggdsResultException
    {
        if ( !this.m_streamMode )
        {
            throw new WggdsResultException("Stream mode is not active. Call startRecordResult first.");
        }
        this.m_resultBuffer.append("\n</queryResult>");
        this.m_streamMode = false;
        this.m_finished = true;
        return this.m_resultBuffer.toString();
    }
    
    public boolean isFinished()
    {
    	return this.m_finished;
    }
    
    public boolean isStreaming()
    {
    	return this.m_streamMode;
    }

    public void addResult(QueryResult a_result, boolean a_completeInformation) throws WggdsResultException
    {
        if ( !this.m_streamMode )
        {
            throw new WggdsResultException("Stream mode is not active. Call startRecordResult first.");
        }
        if ( this.m_writtenErrors )
        {
            throw new WggdsResultException("An errors was written to the xml. All results have to be added BEFORE the error.");
        }
        Element t_structure = new Element("structure");
        t_structure.setAttribute("id", a_result.getId());
        if ( a_completeInformation )
        {
            // url
            if ( a_result.getUrl() != null )
            {
                t_structure.setAttribute("url",a_result.getUrl());
            }
            // glyde
            Element t_glyde = new Element("sequence");
            t_glyde.setText(a_result.getSequence());
            t_structure.addContent(t_glyde);
            // biological source
            if ( a_result.getBiologicalSource().size() > 0 )
            {
                Element t_biologcalSource = new Element("biologicalSource");
                for (BiologicalContext t_context : a_result.getBiologicalSource())
                {
                    Element t_contextElement = new Element("biologicalContext");
                    if (t_context.getNcbiTaxonomyId() != null && t_context.getTaxonomyName() != null )
                    {
                        Element t_sub = new Element("taxonomy");
                        t_sub.setAttribute("ncbiId",t_context.getNcbiTaxonomyId());
                        t_sub.setAttribute("name",t_context.getTaxonomyName());
                        t_contextElement.addContent(t_sub);
                    }
                    if (t_context.getTissueMeshId() != null && t_context.getTissueName() != null )
                    {
                        Element t_sub = new Element("tissueTaxonomy");
                        t_sub.setAttribute("meshId",t_context.getTissueMeshId());
                        t_sub.setAttribute("name",t_context.getTissueName());
                        t_contextElement.addContent(t_sub);
                    }
                    t_biologcalSource.addContent(t_contextElement);
                }
                t_structure.addContent(t_biologcalSource);
            }
        }
        // export to string and add in buffer
        Format t_objFormat = Format.getPrettyFormat();
        XMLOutputter t_exportXML = new XMLOutputter(t_objFormat);
        StringWriter t_writer = new StringWriter();
        try
        {
            t_exportXML.output(t_structure, t_writer );
        } 
        catch (IOException t_exception)
        {
            throw new WggdsResultException(t_exception.getMessage(),t_exception);
        }
        this.m_resultBuffer.append(t_writer.toString());
        this.m_resultBuffer.append("\n");
    }

    public void addError(WggdsError a_error) throws WggdsResultException
    {
        if ( !this.m_streamMode )
        {
            throw new WggdsResultException("Stream mode is not active. Call startRecordResult first.");
        }
        Element t_error = new Element("error");
        t_error.setAttribute("id",a_error.getId().getId().toString());
        Element t_message = new Element("message");
        t_message.setText(a_error.getMessage());
        t_error.addContent(t_message);
        // export to string and add in buffer
        Format t_objFormat = Format.getPrettyFormat();
        XMLOutputter t_exportXML = new XMLOutputter(t_objFormat);
        StringWriter t_writer = new StringWriter();
        try
        {
            t_exportXML.output(t_error, t_writer );
        } 
        catch (IOException t_exception)
        {
            throw new WggdsResultException(t_exception.getMessage(),t_exception);
        }
        this.m_resultBuffer.append(t_writer.toString());
        this.m_writtenErrors = true;
    }

    public String createResult(List<QueryResult> a_resultList,
            List<WggdsError> a_error, String a_listId,
            boolean a_completeInformation, String a_glydeVersion)
            throws WggdsResultException
    {
        if ( a_glydeVersion == null )
        {
            this.startRecordResult(a_listId);
        }
        else
        {
            this.startRecordResult(a_listId,a_glydeVersion);
        }
        for (QueryResult t_queryResult : a_resultList)
        {
            this.addResult(t_queryResult, a_completeInformation);
        }
        for (WggdsError t_wggdsError : a_error)
        {
            this.addError(t_wggdsError);
        }
        return this.finishRecordResult();
    }
    
    public static String generateRandomId(){
    	return UUID.randomUUID().toString();
    }
}
